#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include "GRWLock.h"
#include "GError.h"
#include "GThread.h"
#include "GConditionVar.h"

namespace GThread
{
	#define G_OWNER_ID_NONE	(GUInt32)0xFFFFFFFF

	// For catching recursive locks in debug mode
	static G_THREAD_LOCAL GUInt32 OwnerReadId = G_OWNER_ID_NONE;
	static G_THREAD_LOCAL GUInt32 OwnerWriteId = G_OWNER_ID_NONE;

	void GRWLock::CheckLockFreeCount()
	{
		GUInt32 FinalReadLocks = ReadLocks.GetCount();
		GUInt32 FinalReadFrees = ReadFrees.GetCount();
		GUInt32 FinalWriteLocks = WriteLocks.GetCount();
		GUInt32 FinalWriteFrees = WriteFrees.GetCount();

		GUtil::GError::DebugCheck(FinalReadLocks != FinalReadFrees, "Read lock count does not match read free count!");
		GUtil::GError::DebugCheck(FinalReadLocks != FinalReadFrees, "Write lock count does not match write free count!");

		GUtil::GError::DebugCheck(OwnerReadId != G_OWNER_ID_NONE, "Read lock is still owned by Thread:%d", OwnerWriteId);
		GUtil::GError::DebugCheck(OwnerReadId != G_OWNER_ID_NONE, "Write lock is still owned by Thread:%d", OwnerWriteId);
	}

	void GRWLock::FreeRead()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerReadId == G_OWNER_ID_NONE, "RWLock is not locked for read.");
		ReleaseSRWLockShared((PSRWLOCK)Lock);

		ReadFrees.Increment();

		OwnerReadId = G_OWNER_ID_NONE;
	}

	void GRWLock::FreeWrite()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerWriteId == G_OWNER_ID_NONE, "RWLock is not locked for read.");
		ReleaseSRWLockExclusive((PSRWLOCK)Lock);

		WriteFrees.Increment();

		OwnerWriteId = G_OWNER_ID_NONE;
	}

	void GRWLock::AcquireRead()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerReadId != G_OWNER_ID_NONE, "RWLock is is being locked(Read) recursively!");
		AcquireSRWLockShared((PSRWLOCK)Lock);

		ReadLocks.Increment();

		OwnerReadId = GThread::GetCurrentThreadId();
	}

	void GRWLock::AcquireWrite()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerWriteId != G_OWNER_ID_NONE, "RWLock is is being locked(Write) recursively!");
		AcquireSRWLockExclusive((PSRWLOCK)Lock);

		WriteLocks.Increment();

		OwnerWriteId = GThread::GetCurrentThreadId();
	}

	bool GRWLock::TryAcquireRead()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerReadId != G_OWNER_ID_NONE, "RWLock is is being locked(Read) recursively!");

		bool Result = TryAcquireSRWLockShared((PSRWLOCK)Lock) != 0;
		if (Result)
		{
			ReadLocks.Increment();
			OwnerReadId = GThread::GetCurrentThreadId();
		}

		return Result;
	}

	bool GRWLock::TryAcquireWrite()
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerWriteId != G_OWNER_ID_NONE, "RWLock is is being locked(Write) recursively!");

		bool Result = TryAcquireSRWLockExclusive((PSRWLOCK)Lock) != 0;
		if (Result)
		{
			WriteLocks.Increment();
			OwnerWriteId = GThread::GetCurrentThreadId();
		}
		
		return Result;
	}

	bool GRWLock::WaitRead(GConditionVar* CondVar, GUInt32 TimeOut)
	{
		GUtil::GError::DebugCheck(!IsCreated( ), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerReadId != G_OWNER_ID_NONE, "RWLock may be locked(Read) recursively after the wait!");

		BOOL Result = SleepConditionVariableSRW((PCONDITION_VARIABLE)CondVar->Variable, (PSRWLOCK)Lock, TimeOut, CONDITION_VARIABLE_LOCKMODE_SHARED);
		if (Result == 0)
		{
			GUtil::GError::Error(GUtil::GErrorMessageWin32(GetLastError()));
			return false;
		}
		
		ReadLocks.Increment();
		OwnerReadId = GThread::GetCurrentThreadId();

		return true;
	}

	bool GRWLock::WaitWrite(GConditionVar* CondVar, GUInt32 TimeOut)
	{
		GUtil::GError::DebugCheck(!IsCreated(), "RWLock is not created.");
		GUtil::GError::DebugCheck(OwnerWriteId != G_OWNER_ID_NONE, "RWLock may be locked(Write) recursively after the wait!");

		BOOL Result = SleepConditionVariableSRW((PCONDITION_VARIABLE)CondVar->Variable, (PSRWLOCK)Lock, TimeOut, 0);
		if (Result == 0)
		{
			GUtil::GError::Error(GUtil::GErrorMessageWin32(GetLastError()));
			return false;
		}

		WriteLocks.Increment();
		OwnerWriteId = GThread::GetCurrentThreadId();

		return true;
	}

	bool GRWLock::Create()
	{
		if (IsCreated())
		{
			return false;
		}

		Lock = new SRWLOCK;

		InitializeSRWLock((PSRWLOCK)Lock);

		return true;
	}
}